/**
 * 
 */
package main;

import java.util.Iterator;
import java.util.Vector;

import main.Grid.Heuristic;

import display.MainGamePanel;
import display.MainGamePanel.AgentColor;

/**
 * @author ES
 *
 */
public class Agent {
	public enum AgentName {HUMAN, MINMAX, GREEDY, PRIORITY, WEIGHTED_MINMAX, WEIGHTED};

	private AgentName name;
	private AgentColor color;
	private Heuristic heuristic;
	private int MinMaxLevel = 3;
	/**
	 * 
	 */
	public Agent(AgentName name, AgentColor color) {
		// TODO Auto-generated constructor stub
		setName(name);
		setColor(color);
	}
	

	//Called when it's the agent's turn to play
	public boolean play() {
		// TODO Auto-generated method stub
		Vector<Move> moves = MainGamePanel.GetInstance().getCurrentGrid().GetLegalMoves(getColorChar());
		
		switch(name) //Choose the corresponding heuristics
		{
			case MINMAX:
				minMaxHeuristics(moves);
				break;
			case GREEDY:
				greedyHeuristics(moves);
				break;
			case PRIORITY:
				priorityHeuristics(moves);
				break;
			case WEIGHTED:
				weighted(moves);
				break;
			case WEIGHTED_MINMAX:
				weightedMinMax(moves);
			default:
				break;
				
			
		}
		return false;
	}


	public void setMinMaxLevel(int level){
		MinMaxLevel = level;
	}
	
	private void priorityHeuristics(Vector<Move> moves) {
		@SuppressWarnings("unchecked")
		Vector<Move> updatedMoves = (Vector<Move>)moves.clone();
		char cl = (color == AgentColor.BLACK) ? 'B' : 'W'; 
		Grid currentGrid = new Grid(MainGamePanel.GetInstance().getCurrentGrid());
		currentGrid.setHeuristic(heuristic);
		for(Move m : updatedMoves){
			applyMoveToGrid(currentGrid, m, cl, MinMaxLevel - 1, 1);
		}
		if(moves != null && moves.size() > 0) //If there are legal moves
		{
			Move bestMove = getBestMove(updatedMoves);
			MainGamePanel.GetInstance().getCurrentGrid().setElementAt(bestMove.getRow(), bestMove.getCol(), getColorChar());
		}
	}


	//MinMax strategy
	public void minMaxHeuristics(Vector<Move> moves) 
	{
		@SuppressWarnings("unchecked")
		Vector<Move> updatedMoves = (Vector<Move>)moves.clone();
		char cl = (color == AgentColor.BLACK) ? 'B' : 'W'; 
		for(Move m : updatedMoves){
			applyMoveToGrid(MainGamePanel.GetInstance().getCurrentGrid(), m, cl, MinMaxLevel - 1, 1);
		}
		if(moves != null && moves.size() > 0) //If there are legal moves
		{
			Move bestMove = getBestMove(updatedMoves);
			MainGamePanel.GetInstance().getCurrentGrid().setElementAt(bestMove.getRow(), bestMove.getCol(), getColorChar());
		}
	}
	
	
	private void weighted(Vector<Move> moves) {
		@SuppressWarnings("unchecked")
		Vector<Move> updatedMoves = (Vector<Move>)moves.clone();
		if(moves != null && moves.size() > 0) //If there are legal moves
		{
			Move bestMove = getBestWeightedMove(updatedMoves);
			MainGamePanel.GetInstance().getCurrentGrid().setElementAt(bestMove.getRow(), bestMove.getCol(), getColorChar());
		}
		
	}
	
	private void weightedMinMax(Vector<Move> moves) {
		@SuppressWarnings("unchecked")
		Vector<Move> updatedMoves = (Vector<Move>)moves.clone();
		char cl = (color == AgentColor.BLACK) ? 'B' : 'W'; 
		for(Move m : updatedMoves){
			applyWeightedMinMaxMoveToGrid(MainGamePanel.GetInstance().getCurrentGrid(), m, cl, MinMaxLevel - 1, 1);
		}
		if(moves != null && moves.size() > 0) //If there are legal moves
		{
			Move bestMove = getBestWeightedMinMaxMove(updatedMoves);
			MainGamePanel.GetInstance().getCurrentGrid().setElementAt(bestMove.getRow(), bestMove.getCol(), getColorChar());
		}
		
	}
	
	private void applyMoveToGrid(Grid grid, Move m, char col,
			int level, int flipflop) {
		char nextColor = (col == 'B') ? 'W' : 'B';
		int flip = flipflop * -1;
		Grid tempGrid = new Grid(grid);
		tempGrid.setElementAt(m.getRow(), m.getCol(), col);
		
		Vector<Move> moves = tempGrid.GetLegalMoves(nextColor);
		if(moves.size() != 0){
			Move bestMove = getBestMove(moves);
			bestMove.setScore(bestMove.getScore() * flip);
			if(level != 0){
				applyMoveToGrid(tempGrid, bestMove, nextColor, level -1, flip);
			}
			m.setScore(m.getScore() + bestMove.getScore());
		}
		
	}

	
	private void applyWeightedMinMaxMoveToGrid(Grid grid, Move m, char col,
			int level, int flipflop) {
		char nextColor = (col == 'B') ? 'W' : 'B';
		int flip = flipflop * -1;
		Grid tempGrid = new Grid(grid);
		tempGrid.setElementAt(m.getRow(), m.getCol(), col);
		
		Vector<Move> moves = tempGrid.GetLegalMoves(nextColor);
		if(moves.size() != 0){
			Move bestMove = getBestWeightedMinMaxMove(moves);
			bestMove.setScore(bestMove.getScore() * flip);
			if(level != 0){
				applyWeightedMinMaxMoveToGrid(tempGrid, bestMove, nextColor, level -1, flip);
			}
			m.setScore(m.getScore() + bestMove.getScore());
		}
		
	}


	//Greedy strategy: choose the move that maximises score in depth 1
	public void greedyHeuristics(Vector<Move> moves)
	{
		
		if(moves != null && moves.size() > 0) //If there are legal moves
		{
			Move bestMove = getBestMove(moves);
			MainGamePanel.GetInstance().getCurrentGrid().setElementAt(bestMove.getRow(), bestMove.getCol(), getColorChar());
		}
	}
	
	
	
	@SuppressWarnings("rawtypes")
	public Move getBestMove(Vector<Move> moves)
	{
		if(moves.size() == 0){
			System.out.println("Wat");
		}
		Iterator it1 = moves.iterator();
		Move highestMoveScore = (Move)it1.next();
		Move toCompare;
		Vector<Move> sameScore = new Vector<Move>();
		sameScore.add(highestMoveScore);

		while(it1.hasNext())
		{
			toCompare = (Move)it1.next();
			if(toCompare.getScore() > highestMoveScore.getScore()){
				highestMoveScore = toCompare.clone();
				sameScore.clear();
				sameScore.add(highestMoveScore);
			}else if(toCompare.getScore() == highestMoveScore.getScore()){
				sameScore.add(toCompare);
			}
		}
		int index = (int)(Math.random() * sameScore.size());
		
		//return highestMoveScore;
		return sameScore.get(index);
		
	}
	
	
	@SuppressWarnings("rawtypes")
	public Move getBestWeightedMove(Vector<Move> moves)
	{
		if(moves.size() == 0){
			System.out.println("Wat");
		}
		Iterator it1 = moves.iterator();
		
		Move highestMoveScore = (Move)it1.next();
		highestMoveScore.setScore(
				highestMoveScore.getScore() * Grid.getMoveWeight(highestMoveScore.getRow(), highestMoveScore.getCol()));
		Move toCompare;
		Vector<Move> sameScore = new Vector<Move>();
		sameScore.add(highestMoveScore);

		while(it1.hasNext())
		{
			toCompare = (Move)it1.next();
			toCompare.setScore(
					toCompare.getScore() * Grid.getMoveWeight(toCompare.getRow(), toCompare.getCol()));
			
			if(toCompare.getScore() > highestMoveScore.getScore()){
				highestMoveScore = toCompare.clone();
				sameScore.clear();
				sameScore.add(highestMoveScore);
			}else if(toCompare.getScore() == highestMoveScore.getScore()){
				sameScore.add(toCompare);
			}
		}
		int index = (int)(Math.random() * sameScore.size());
		return sameScore.get(index);
		
	}
	
	
	@SuppressWarnings("rawtypes")
	public Move getBestWeightedMinMaxMove(Vector<Move> moves)
	{
		if(moves.size() == 0){
			System.out.println("Wat");
		}
		Iterator it1 = moves.iterator();
		
		Move highestMoveScore = (Move)it1.next();
		if(highestMoveScore.getScore() > 0)
			highestMoveScore.setScore(
				highestMoveScore.getScore() * Grid.getMoveWeight(highestMoveScore.getRow(), highestMoveScore.getCol()));
		else
		{
			if(Grid.getMoveWeight(highestMoveScore.getRow(), highestMoveScore.getCol()) < 0)
				highestMoveScore.setScore(
						highestMoveScore.getScore()*5); //Increase the score so it won't be chosen by the algorithm unless it's the least one
		}
		Move toCompare;
		Vector<Move> sameScore = new Vector<Move>();
		sameScore.add(highestMoveScore);

		while(it1.hasNext())
		{
			toCompare = (Move)it1.next();
			if(toCompare.getScore() > 0)
				toCompare.setScore(
					toCompare.getScore() * Grid.getMoveWeight(toCompare.getRow(), toCompare.getCol()));
			else
			{
				if(Grid.getMoveWeight(highestMoveScore.getRow(), highestMoveScore.getCol()) < 0)
					highestMoveScore.setScore(
							highestMoveScore.getScore()*2); //Increase the score so it won't be chosen by the algorithm unless it's the least one
			}
			
			if(toCompare.getScore() > highestMoveScore.getScore()){
				highestMoveScore = toCompare.clone();
				sameScore.clear();
				sameScore.add(highestMoveScore);
			}else if(toCompare.getScore() == highestMoveScore.getScore()){
				sameScore.add(toCompare);
			}
		}
		int index = (int)(Math.random() * sameScore.size());
		return sameScore.get(index);
		
	}
	
	
	//Returns the color of the agent as one character
	public char getColorChar() {
		
		switch(color)
		{
			case WHITE:
				return 'W';
			case BLACK:
				return 'B';
			default:
				return ' ';
		}
	}
	
	/**
	 * @return the color
	 */
	public AgentColor getColor() {
		return color;
	}


	/**
	 * @param color the color to set
	 */
	public void setColor(AgentColor color) {
		this.color = color;
	}

	
	/**
	 * @return the name
	 */
	public AgentName getName() {
		return name;
	}
	/**
	 * @param name the name to set
	 */
	public void setName(AgentName name) {
		this.name = name;
	}


	public void setHeuristic(Heuristic selectedItem) {
		heuristic = selectedItem;
	}

}
